package top.wshape1.rpc;

import lombok.extern.slf4j.Slf4j;
import top.wshape1.rpc.common.constant.*;
import top.wshape1.rpc.config.JJRpcConfig;
import top.wshape1.rpc.config.RegistryConfig;
import top.wshape1.rpc.consumer.RpcConsumer;
import top.wshape1.rpc.filter.Filter;
import top.wshape1.rpc.filter.FilterChainFactory;
import top.wshape1.rpc.message.serializer.SerializerFactory;
import top.wshape1.rpc.provider.RpcProvider;
import top.wshape1.rpc.provider.service.RpcServiceFactory;
import top.wshape1.rpc.registry.RpcRegistry;
import top.wshape1.rpc.registry.RpcRegistryFactory;

import java.util.Collection;

/**
 * @author Wshape1
 * @version 1.0
 * @description 使用JJ-RPC的入口
 * @since 2024-02-02
 */

@Slf4j
public class JJRPC {

    /*
     * Fields
     */

    private String serviceHost;
    private int servicePort;
    private boolean isConsumer;
    private boolean isProvider;
    private RpcRegistry registry;
    private RpcProvider provider;
    private RpcConsumer consumer;

    /*
     * Constructors
     */

    public JJRPC() {
    }


    public JJRPC(String serviceHost, int servicePort) {
        serviceHost(serviceHost).servicePort(servicePort);
    }

    /*
     * Common Settings
     */

    /**
     * 设置本机地址
     *
     * @param serviceHost 服务地址
     * @return this
     */
    public JJRPC serviceHost(String serviceHost) {
        JJRpcConfig.CONFIG.setHost(serviceHost);
        return this;
    }

    /**
     * 设置本机端口
     *
     * @param servicePort 服务端口
     * @return this
     */
    public JJRPC servicePort(int servicePort) {
        JJRpcConfig.CONFIG.setPort(servicePort);
        return this;
    }

    /**
     * token
     *
     * @param token token string
     * @return this
     */
    public JJRPC token(String token) {
        JJRpcConfig.CONFIG.setToken(token);
        return this;
    }

    /**
     * 设置注册中心
     *
     * @param type {@link RegistryType}
     * @return this
     */
    public JJRPC registry(RegistryType type) {
        return registry(type.name());
    }

    /**
     * 使用键值设置注册中心
     *
     * @param type 注册中心的键
     * @return this
     */
    public JJRPC registry(String type) {
        JJRpcConfig.CONFIG.getRegistry().setType(type);
        return this;
    }

    /**
     * 设置RedisConfig
     *
     * @param config {@link RegistryConfig.RedisRegistryConfig}
     * @return this
     */
    public JJRPC redisRegistry(RegistryConfig.RedisRegistryConfig config) {
        JJRpcConfig.CONFIG.getRegistry().setRedis(config);
        return this;
    }

    /**
     * 设置zookeeperConfig
     *
     * @param config {@link RegistryConfig.ZookeeperRegistryConfig}
     * @return this
     */
    public JJRPC zookeeperRegistry(RegistryConfig.ZookeeperRegistryConfig config) {
        JJRpcConfig.CONFIG.getRegistry().setZookeeper(config);
        return this;
    }

    /**
     * 设置nacosConfig
     *
     * @param config {@link RegistryConfig.NacosRegistryConfig}
     * @return this
     */
    public JJRPC nacosRegistry(RegistryConfig.NacosRegistryConfig config) {
        JJRpcConfig.CONFIG.getRegistry().setNacos(config);
        return this;
    }

    /*
     * Serializer Settings
     */

    /**
     * 设置消息序列化器
     *
     * @param type {@link SerializerType}
     * @return this
     */
    public JJRPC serializer(SerializerType type) {
        return serializer(type.name());
    }

    /**
     * 使用键值设置序列化器
     *
     * @param type 序列化器的键
     * @return this
     */
    public JJRPC serializer(String type) {
        JJRpcConfig.CONFIG.getSerializer().setType(type);
        return this;
    }

    /*
     * Consumer Settings
     */

    /**
     * 是否注册为消费者
     *
     * @param isConsumer true or false
     * @return this
     */
    public JJRPC consumer(boolean isConsumer) {
        JJRpcConfig.CONFIG.getConsumer().setEnabled(isConsumer);
        return this;
    }

    /**
     * 设置调用超时时间
     *
     * @param callTimeout CallTimeout, Unit: ms
     * @return this
     */
    public JJRPC callTimeout(int callTimeout) {
        JJRpcConfig.CONFIG.getConsumer().setCallTimeout(callTimeout);
        return this;
    }

    /**
     * 设置负载均衡
     *
     * @param type {@link LoadBalanceType}
     * @return this
     */
    public JJRPC loadBalance(LoadBalanceType type) {
        return loadBalance(type.name());
    }

    /**
     * 使用键值设置负载均衡
     *
     * @param type LoadBalance's key
     * @return this
     */
    public JJRPC loadBalance(String type) {
        JJRpcConfig.CONFIG.getConsumer().setLoadBalance(type);
        return this;
    }

    /*
     * Provider Settings
     */

    /**
     * 是否注册为提供者
     *
     * @param isProvider true or false
     * @return this
     */
    public JJRPC provider(boolean isProvider) {
        JJRpcConfig.CONFIG.getProvider().setEnabled(isProvider);
        return this;
    }

    /*
     * Filter Settings
     */

    /**
     * 添加Consumer发送请求的Filter
     *
     * @param collection List of Filter
     * @return this
     */
    public JJRPC addConsumerSendFilter(Collection<Filter> collection) {
        for (Filter filter : collection) {
            FilterChainFactory.getConsumerSendReqFilterChain().addFilter(filter);
        }
        return this;
    }

    /**
     * 添加Consumer接收响应的Filter
     *
     * @param collection List of Filter
     * @return this
     */
    public JJRPC addConsumerRecFilter(Collection<Filter> collection) {
        for (Filter filter : collection) {
            FilterChainFactory.getConsumerRecRespFilterChain().addFilter(filter);
        }
        return this;
    }

    /**
     * 添加Provider发送响应的Filter
     *
     * @param collection List of Filter
     * @return this
     */
    public JJRPC addProviderSendFilter(Collection<Filter> collection) {
        for (Filter filter : collection) {
            FilterChainFactory.getProviderSendRespFilterChain().addFilter(filter);
        }
        return this;
    }

    /**
     * 添加Provider接收请求的Filter
     *
     * @param collection List of Filter
     * @return this
     */
    public JJRPC addProviderRecFilter(Collection<Filter> collection) {
        for (Filter filter : collection) {
            FilterChainFactory.getProviderRecReqFilterChain().addFilter(filter);
        }
        return this;
    }

    /*
     * Fault Tolerant Settings
     */

    /**
     * 设置容错策略
     *
     * @param type {@link FaultTolerantEnum}
     * @return this
     */
    public JJRPC faultTolerant(FaultTolerantEnum type) {
        return faultTolerant(type.name());
    }

    /**
     * 使用键值设置容错策略
     *
     * @param strategy FaultTolerance's key
     * @return this
     */
    public JJRPC faultTolerant(String strategy) {
        JJRpcConfig.CONFIG.getConsumer().getFaultTolerant().setStrategy(strategy);
        return this;
    }

    /**
     * 设置重试次数，只有在failback和failover策略下有效
     *
     * @param retryTimes RetryTimes
     * @return this
     */
    public JJRPC retryTimes(int retryTimes) {
        JJRpcConfig.CONFIG.getConsumer().getFaultTolerant().setRetryTimes(retryTimes);
        return this;
    }

    /**
     * 设置重试间隔时间，单位：ms,只有在failback和failover策略下有效
     *
     * @param retryInterval RetryInterval, Unit: ms
     * @return this
     */
    public JJRPC retryInterval(int retryInterval) {
        JJRpcConfig.CONFIG.getConsumer().getFaultTolerant().setRetryInterval(retryInterval);
        return this;
    }

    /*
     * Compress Settings
     */

    /**
     * 是否开启数据压缩，默认关闭
     *
     * @param enable true or false
     * @return this
     */
    public JJRPC compress(boolean enable) {
        JJRpcConfig.CONFIG.getCompress().setEnabled(enable);
        return this;
    }

    /**
     * 设置数据压缩器
     *
     * @param compressor key
     * @return this
     */
    public JJRPC compressor(String compressor) {
        JJRpcConfig.CONFIG.getCompress().setCompressor(compressor);
        return this;
    }

    /**
     * 设置数据压缩器
     *
     * @param type {@link CompressorType}
     * @return this
     */
    public JJRPC compressor(CompressorType type) {
        return compressor(type.name());
    }

    /*
     * Operations
     */

    /**
     * 服务配置并启动JJRPC
     *
     * @return this
     */
    public JJRPC start() {
        long startTime = System.currentTimeMillis();

        // 获取配置
        isConsumer = JJRpcConfig.CONFIG.getConsumer().isEnabled();
        isProvider = JJRpcConfig.CONFIG.getProvider().isEnabled();

        initAndCheckAddress();
        // 验证配置
        assert isConsumer || isProvider : "Consumer or provider must be selected";

        initRegistry();

        log.info("Registry Class：'{}'", registry.getClass().getName());
        initProvider();
        initConsumer();
        log.info("Provider/Consumer: '{}'", serviceHost + ":" + servicePort);
        log.info("JJRPC is done! cost time: {}s", (System.currentTimeMillis() - startTime) / 1000.0);

        return this;
    }

    public void startProvider() {
        if (provider == null) {
            isProvider = true;
            initAndCheckAddress();
            initRegistry();
            initProvider();
        }
    }

    public void startConsumer() {
        if (consumer == null) {
            isConsumer = true;
            initAndCheckAddress();
            initRegistry();
            initConsumer();
        }
    }

    /**
     * 停止JJRPC
     */
    public void stop() {

        log.info("JJRPC is stopping...");

        // 注销

        if (provider != null) {
            try {
                RpcServiceFactory.unregisterAllService();
            } catch (Throwable t) {
                log.warn(t.getMessage(), t);
            }
            try {
                provider.stop();
            } catch (Throwable t) {
                log.warn(t.getMessage(), t);
            }
        }
        if (consumer != null) {
            try {
                consumer.stop();
            } catch (Throwable t) {
                log.warn(t.getMessage(), t);
            }
        }
        if (registry != null) {
            try {
                registry.destroy();
            } catch (Throwable t) {
                log.warn(t.getMessage(), t);
            }
        }
        try {
            SerializerFactory.resetFactory();
        } catch (Throwable t) {
            log.warn(t.getMessage(), t);
        }
    }

    /**
     * 注册服务接口，开启provider可用且要在设置Registry之后
     *
     * @param serviceInterfaceClass 接口类 类型
     * @param serviceImplementation 接口的实现类
     * @param <T>                   接口类型
     */
    public <T> void registerService(Class<T> serviceInterfaceClass, T serviceImplementation) {
        if (isProvider) {
            if (registry == null) {
                log.error("Registry must be set before registering service.");
                throw new RuntimeException("Registry must be set before registering service.");
            }
            RpcServiceFactory.registryService(serviceInterfaceClass, serviceImplementation);
            log.info("Interface service '{}' is successfully registered. The implementation is '{}'",
                    serviceInterfaceClass.getName(),
                    serviceImplementation.getClass().getName());
        } else {
            log.warn("Ignored this register, because provider must be set before registering service.");
        }
    }

    /**
     * 获取远程服务的代理对象
     *
     * @param serviceInterfaceClass 要代理的接口类
     * @param <T>                   接口类型
     * @return {@link T}
     */
    public <T> T getService(Class<T> serviceInterfaceClass) {
        if (consumer == null) {
            log.error("Getting service return null, because consumer must be set before getting service.");
            throw new RuntimeException("Consumer must be set before getting service.");
        }
        return consumer.getProxyService(serviceInterfaceClass);
    }

    private void initConsumer() {
        if (isConsumer) {
            // 创建服务消费者
            log.info("Creating service consumer...");
            if (consumer == null) {
                consumer = new RpcConsumer();
            }
            log.info("The service consumer is created.");
        }
    }

    private void initProvider() {
        if (isProvider) {
            // 创建服务提供者
            log.info("Creating service provider...");
            if (provider == null) {
                provider = new RpcProvider(servicePort);
            }
            log.info("The service provider is created.");
        }
    }

    private void initRegistry() {
        if (registry == null) {
            registry = RpcRegistryFactory.getRegistry();
        }
    }


    private void initAndCheckAddress() {
        if (serviceHost == null) {
            serviceHost = JJRpcConfig.CONFIG.getHost();
            assert serviceHost != null : "Service host cannot be empty";
        }
        if (servicePort == 0) {
            servicePort = JJRpcConfig.CONFIG.getPort();
            assert servicePort > 0 && servicePort < 65535 : "Service port must be greater than 0 and less than 65535";
        }
    }

}
